# coding: utf-8

import json

from flask_restful import Api
from flask import request, current_app

from sqlalchemy import or_, and_

from app.api.codes import PARAMS_ILLEGAL
from app.api_v2.codes import INVITE_NOT_EXISTED, INVITE_SELF, INVITED_SELF, \
    NOT_YOUR_FRIEND, MSG_REPLY_FAILED
from app.api_v2.constant import BlacklistPageType, \
    BlackValue, RECOMMEND_USER_LIST_KEY
from app.api_v2.relationship_schemas import mobile_update_schema, friend_schema, \
    invite_pass_schema, invite_list_schema, invite_user_schema, \
    friend_name_update_schema, invite_msg_schema, \
    blacklist_schema
from app.api_v2.utils import Pagination
from app.api_v2.views import AppAuthResource
from app.api_v2.views import Response
from app.msg.utils import generate_sms_content
from app.op.models import User, Follow, db
from app.relationship.constant import FRIEND_LIST_KEY, FriendStatus, \
    BLACKLIST_KEY
from app.relationship.models import InviteLog, MobileList, InviteMessage
from app.utils import MsgProxy

api = Api()


@api.resource('/user/mobile_lists')
class UserMobileListResource(AppAuthResource):
    """
    create by ld_wu 2018-01-08
    用户通讯录
    """

    def put(self):
        """
        更新通讯录/上传通讯录
        :return:
        """
        data, errors = mobile_update_schema.load(request.get_json())
        if errors:
            return Response(
                PARAMS_ILLEGAL.new_format_msg(list(errors.values())[0][0])
            )
        mobile_list = data['mobile_list']
        phones = [each.get('mobile', '') for each in mobile_list]
        users = User.get_user_by_mobiles(phones)
        user_mapping = {each.mobile: each for each in users}
        MobileList.insert_mobile_list(
            self.user.id, mobile_list, user_mapping, phones
        )
        return Response()

    def get(self):
        """
        通讯录列表
        :return:
        """

        # 可能认识（所有都已注册）
        query = MobileList.query.filter(
            MobileList.deleted_at == 0
        )
        query = query.filter(
            MobileList.follower_id == self.user.id,
            MobileList.for_recommend.is_(False)
        )
        mobile_lists = query.all()

        user_ids = set()

        for each in mobile_lists:
            user_ids.add(each.follower_id)
            user_ids.add(each.followed_id)
        user_mapping = User.user_mapping(list(user_ids))
        info_list = [
            each.extend_user_info(self.user.id, user_mapping)
            for each in mobile_lists
        ]

        return Response(body=dict(users=info_list))

    def post(self):
        """
        发送邀请短信
        发送短信不修改通讯录状态（依然是可邀请状态）
        :return:
        """
        data, errors = invite_user_schema.load(request.get_json())
        if errors:
            return Response(
                PARAMS_ILLEGAL.new_format_msg(list(errors.values())[0][0])
            )
        mobile = data['mobile']
        if mobile == self.user.mobile:
            return Response(INVITED_SELF)
        user = User.get_by_args(mobile=mobile)
        if not user:
            template_code = 'INVITE_MSG'
            content = generate_sms_content(
                template_code=template_code,
                nickname=self.user.nickname,
                user_id=self.user.id
            )
            current_app.wph_sms_producer.send(
                json.dumps({
                    'mobile': mobile,
                    'content': content,
                })
            )
        return Response()


@api.resource('/invite/friend/<int:followed_id>')
class InviteFriendResource(AppAuthResource):
    """
    create by ld_wu 2018-01-08
    """

    def post(self, followed_id):
        """
        邀请好友申请
        """
        follower_id = self.user.id
        remark = request.get_json().get('remark', '')
        source = request.get_json().get('source', 0)
        if followed_id == follower_id:
            return Response(INVITE_SELF)
        followed = User.get_by_id(followed_id)
        if not followed:
            return Response()
        friend_list_key = FRIEND_LIST_KEY.format(user_id=follower_id)
        if not current_app.redis.sismember(friend_list_key, followed_id):
            InviteLog.invite_friend(follower_id, followed_id, remark, source)

        return Response()


@api.resource('/invite/message/content')
class InviteMessageContentResource(AppAuthResource):
    """
    created by chenhj at 2018/6/14

    """
    def get(self):
        """
        created by chenhj at 2018/6/14
        获取
        """
        template_code = 'INVITE_MSG'
        content = generate_sms_content(
            template_code=template_code,
            nickname=self.user.nickname,
            user_id=self.user.id
        )
        return Response(body=content)


@api.resource('/invite/messages')
class InviteMessageResource(AppAuthResource):
    """
    create by ld_wu 2018-01-08
    """

    def post(self):
        """
        好友邀请消息回复
        """
        data, errors = invite_msg_schema.load(request.get_json())
        if errors:
            return Response(
                PARAMS_ILLEGAL.new_format_msg(list(errors.values())[0][0])
            )
        user_id = data['user_id']
        invite_log = InviteLog.query.filter(
            or_(
                and_(
                    InviteLog.followed_id == self.user.id,
                    InviteLog.follower_id == user_id,
                ),
                and_(
                    InviteLog.follower_id == self.user.id,
                    InviteLog.followed_id == user_id
                )
            )
        ).first()
        if not invite_log:
            return Response(MSG_REPLY_FAILED)

        # 非等待认证或者通过认证状态
        if invite_log.status not in (FriendStatus.APPROVAL, FriendStatus.WAITING):
            return Response(MSG_REPLY_FAILED)

        # 如果是对方回复
        to_id = invite_log.followed_id
        if invite_log.follower_id != self.user.id:
            to_id = invite_log.follower_id
        InviteMessage.create(
            from_id=self.user.id,
            to_id=to_id,
            content=data['content']
        )

        return Response()

    def get(self):
        """
        create by ld_wu 2018-03-29
        好友邀请消息列表
        :return:
        """
        data, errors = invite_pass_schema.load(request.args)
        if errors:
            return Response(
                PARAMS_ILLEGAL.new_format_msg(list(errors.values())[0][0])
            )

        user_id = data['user_id']

        group_id = '%s:%s' % tuple(
            sorted([str(user_id), str(self.user.id)])
        )
        query = InviteMessage.query.filter(
            InviteMessage.group_id == group_id
        )
        messages = query.all()

        user_ids = [each.from_id for each in messages]
        users = User.get_list_by_ids(user_ids)
        user_mapping = {each.id: each for each in users}
        message_list = [
            dict(
                from_id=each.from_id,
                content=each.content,
                avatar_url=user_mapping.get(each.from_id).avatar_url,
                nickname=user_mapping.get(each.from_id).nickname
            )
            for each in messages]

        return Response(body=dict(messages=message_list))


@api.resource('/invite/logs')
class InviteLogsResource(AppAuthResource):
    """
    create by ld_wu 2018-01-08
    """

    def get(self):

        """
        添加记录列表
        :return:
        """
        user_id = self.user.id
        query = InviteLog.invite_logs(user_id)
        user_ids = set()
        pagination = Pagination(query, invite_list_schema)
        logs = pagination.items
        for each in logs:
            user_ids.add(each.follower_id)
            user_ids.add(each.followed_id)
        user_mapping = User.user_mapping(list(user_ids))
        log_info = [
            each.extend_user_info(user_id, user_mapping) for each in logs
        ]
        return pagination.to_response(log_info)

    def put(self):
        """
        好友申请通过认证
        :return:
        """
        data, errors = invite_pass_schema.load(request.get_json())
        if errors:
            return Response(
                PARAMS_ILLEGAL.new_format_msg(list(errors.values())[0][0])
            )
        invite_log = InviteLog.get_by_args(
            follower_id=data['user_id'],
            followed_id=self.user.id
        )
        if not invite_log:
            return Response(INVITE_NOT_EXISTED)
        elif invite_log.status != FriendStatus.APPROVAL:
            return Response(INVITE_NOT_EXISTED)
        invite_log.pass_invite()

        # 好友通过以后把原有的黑名单移除
        current_app.redis.srem(
            BLACKLIST_KEY.format(
                user_id=invite_log.followed_id
            ),
            invite_log.follower_id
        )
        current_app.redis.srem(
            BLACKLIST_KEY.format(
                user_id=invite_log.follower_id
            ),
            invite_log.followed_id
        )

        proxy = MsgProxy()
        # im移除黑名单
        proxy.im_client_producer.update_blacklist(
            invite_log.followed_id,
            invite_log.follower_id,
            value=0
        )
        proxy.im_client_producer.update_blacklist(
            invite_log.follower_id,
            invite_log.followed_id,
            value=0
        )
        return Response()


@api.resource('/invite/logs/un_read_count')
class InviteLogCountResource(AppAuthResource):

    def get(self):
        """
        未读的好友申请记录数量
        :return:
        """
        un_read_count = InviteLog.un_read_count(self.user.id)
        return Response(
            body=dict(un_read_count=un_read_count)
        )


@api.resource('/user/friends', '/user/friends/<int:follower_id>')
class UserFriendsResource(AppAuthResource):
    """
    create by ld_wu 2018-01-08
    好友列表
    """

    def get(self):
        friends = Follow.query.filter_by(followed_id=self.user.id)
        data, errors = friend_schema.dump(friends)
        if errors:
            data = list()
        return Response(body=dict(list=data))

    def put(self):
        """
        设置备注名称
        :return:
        """
        data, errors = friend_name_update_schema.load(request.get_json())
        if errors:
            return Response(
                PARAMS_ILLEGAL.new_format_msg(list(errors.values())[0][0])
            )

        follow = Follow.get_by_args(
            followed_id=self.user.id,
            follower_id=data['followed_id']
        )
        if not follow:
            return Response(NOT_YOUR_FRIEND)

        alias = data['alias']

        follow.has_set_alias = True
        follow.alias = alias
        return Response()

    def delete(self, follower_id):
        """
        删除好友
        1、对方未删除，则操作者显示通过认证，对方显示已添加
        2、对方已删除，则双方都显示未添加好友
        :param follower_id:
        :return:
        """

        def update_friend_status(from_id, to_id):

            # 我的通讯录中对方的状态
            MobileList.query.filter(
                MobileList.followed_id == to_id,
                MobileList.follower_id == from_id,
                MobileList.deleted_at == 0,
            ).update(
                dict(status=status),
                synchronize_session='fetch'
            )

            # 对方添加自己的状态修改
            InviteLog.query.filter(
                InviteLog.followed_id == from_id,
                InviteLog.follower_id == to_id
            ).update(
                dict(status=status),
                synchronize_session='fetch'
            )

        # 移除我的好友
        follow = Follow.get_by_args(
            followed_id=self.user.id,
            follower_id=follower_id
        )
        if follow:
            db.session.delete(follow)

        followed_key = FRIEND_LIST_KEY.format(user_id=follower_id)
        if current_app.redis.srem(followed_key, self.user.id):
            # 对方未删我，我的状态相当于对方添加我，我显示通过验证
            status = FriendStatus.APPROVAL
        else:
            # 对方已删，双方状态回到初始值
            status = FriendStatus.DEFAULT
            # 修改对方的状态
            update_friend_status(follower_id, self.user.id)

        update_friend_status(self.user.id, follower_id)

        follower_key = FRIEND_LIST_KEY.format(user_id=self.user.id)
        current_app.redis.srem(follower_key, follower_id)
        # im 加入黑名单
        proxy = MsgProxy()
        proxy.im_client_producer.update_blacklist(
            self.user.id,
            follower_id
        )
        proxy.im_client_producer.update_blacklist(
            follower_id,
            self.user.id
        )
        return Response()


@api.resource('/user/search')
class UserSearchResource(AppAuthResource):
    """
    create by ld_wu 2018-03-29
    """

    def get(self):

        """
        全局搜索好友名片
        :return:
        """

        key_word = request.args.get('key_word')
        if not key_word:
            users = list()
        else:
            users = self.user.followers.filter(
                or_(
                    User.mobile.like('{}%'.format(key_word)),
                    User.nickname.like('%{}%'.format(key_word))
                )
            ).all()

        search_result = [
            dict(
                user_id=each.id,
                nickname=each.nickname,
                avatar_url=each.avatar_url,
                mobile=each.mobile
            ) for each in users
        ]

        return Response(body=dict(users=search_result))


@api.resource('/user/blacklists')
class UserBlacklists(AppAuthResource):
    """
    create by ld_wu 2018-04-10
    """

    def post(self):
        """
        加入黑名单
        :return:
        """
        data, errors = blacklist_schema.load(request.get_json())
        if errors:
            return Response(
                PARAMS_ILLEGAL.new_format_msg(list(errors.values())[0][0])
            )
        user_id = data['user_id']
        page_type = data['page_type']

        blacklist_key = BLACKLIST_KEY.format(user_id=self.user.id)

        if page_type == BlacklistPageType.UNBLOCK:
            value = BlackValue.UNBLOCK
            is_friend = current_app.redis.sismember(
                FRIEND_LIST_KEY.format(user_id=self.user.id), user_id
            )
            # 非好友不能解除im拉黑状态
            # 但是服务器的黑名单列表会移除该用户
            current_app.redis.srem(blacklist_key, user_id)
            if not is_friend:
                return Response()
        elif page_type == BlacklistPageType.BLOCK:
            value = BlackValue.BLOCK
            # 记录用户的黑名单列表
            current_app.redis.sadd(blacklist_key, user_id)
        else:
            return Response()

        # 更新im黑名单
        proxy = MsgProxy()
        proxy.im_client_producer.update_blacklist(
            self.user.id,
            user_id,
            value=value
        )
        return Response()

    def get(self):
        """
        create by ld_wu 2018-04-10
        获取黑名单列表
        :return:
        """
        blacklist_key = BLACKLIST_KEY.format(user_id=self.user.id)
        blacklist = [
            int(user_id)
            for user_id in current_app.redis.smembers(blacklist_key)
        ]
        return Response(body=dict(blacklist=blacklist))


class RecommendBase(AppAuthResource):

    def query_data(self, cache=True):

        query = MobileList.query.filter(
            MobileList.deleted_at == 0
        )

        # 我的通讯录
        follower_query = query.filter(
            MobileList.follower_id == self.user.id,
            MobileList.followed_id.isnot(None),
        )
        followed_ids = [
            each.followed_id
            for each in follower_query.with_entities(MobileList.followed_id)
            if each.followed_id
        ]
        # 对方通讯录有我的
        followed_query = query.filter(
            MobileList.followed_id == self.user.id,
            MobileList.follower_id.notin_(followed_ids)
        )
        mobile_lists = followed_query.all() + follower_query.all()

        # 添加记录（包含我直接邀请的或者邀请我的）
        log_query = InviteLog.invite_logs(self.user.id)

        invite_logs = log_query.all()
        user_ids = {each.follower_id for each in invite_logs}

        for each in mobile_lists:
            extend_id = (
                each.follower_id
                if each.followed_id == self.user.id
                else each.followed_id
            )
            if extend_id is None:
                each.invite_time = each.created_datetime
                invite_logs.append(each)
            elif extend_id not in user_ids:
                user_ids.add(extend_id)
                each.invite_time = each.created_datetime
                invite_logs.append(each)

        result = sorted(invite_logs, key=lambda x: x.invite_time, reverse=True)

        user_mapping = User.user_mapping(list(user_ids))
        info_list = [
            each.extend_user_info(self.user.id, user_mapping)
            for each in result
        ]
        if cache:
            current_app.redis.set(
                RECOMMEND_USER_LIST_KEY.format(self.user.id),
                json.dumps(info_list),
                ex=3600
            )
        return info_list


@api.resource('/recommend/users')
class RecommendUserResource(RecommendBase):
    """
    create by ld_wu 2018-05-22
    拓展人脉
    """

    def get(self):
        first_page = 0
        page = request.args.get('page', first_page, type=int)
        per_page = request.args.get('per_page', 20, type=int)
        if page == first_page:
            info_list = self.query_data()
        else:
            cache_data = current_app.redis.get(
                RECOMMEND_USER_LIST_KEY.format(self.user.id)
            )
            if cache_data:
                info_list = json.loads(cache_data)
            else:
                info_list = self.query_data()

        start = page * per_page
        end = start + per_page
        result = info_list[start: end]
        return Response(body=dict(users=result))


@api.resource('/recommend/search')
class RecommendSearchResource(RecommendBase):
    """
    create by ld_wu 2018-05-22
    拓展人脉搜索
    """

    def get(self):
        cache_data = current_app.redis.get(
            RECOMMEND_USER_LIST_KEY.format(self.user.id)
        )
        if cache_data:
            info_list = json.loads(cache_data)
        else:
            info_list = self.query_data(False)

        return Response(body=dict(users=self.key_word_filter(info_list)))

    @staticmethod
    def key_word_filter(info_list):
        """
        符合手机，昵称，备注的用户
        :param info_list:
        :return:
        """
        key_word = request.args.get('key_word', '')
        results = list()
        if key_word:
            results = [
                each for each in info_list
                if (
                    key_word in each.get('mobile', '')
                    or key_word in each.get('nickname', '')
                )
            ]
        return results
